package com.cloud.gatewaySpringSecurity.utils;

import com.cloud.gatewaySpringSecurity.pojo.Payload;
import io.jsonwebtoken.*;
import org.joda.time.DateTime;

import java.security.PrivateKey;
import java.security.PublicKey;
import java.util.Base64;
import java.util.Date;
import java.util.UUID;

/**
 * @author zhozixiong
 * @date 2022/7/19
 *
 * Token工具类
 */
public class JwtUtil {

    private static final String JWT_KEY = "userInfo";

    /**
     * 默认过期时间
     */
    private static final int EXPIRES_TOKEN = 5 * 60 * 24;

    private JwtUtil() {
    }

    /**
     * 使用uuid生成一个不重复的id
     *
     * @return token唯一id
     */
    public static String createJti() {
        return new String(Base64.getEncoder().encode(UUID.randomUUID().toString().getBytes()));
    }

    /**
     * 使用私钥生成token
     *
     * @param userInfo 载荷中的数据
     * @param privateKey 私钥
     * @return token
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey) {
        return generateTokenExpireInMinutes(userInfo, privateKey, EXPIRES_TOKEN, createJti());
    }

    /**
     * 使用私钥生成token,并设置唯一id
     *
     * @param userInfo 载荷中的数据
     * @param privateKey 私钥
     * @param id token唯一id
     * @return token
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, String id) {
        return generateTokenExpireInMinutes(userInfo, privateKey, EXPIRES_TOKEN, id);
    }

    /**
     * 使用私钥生成token,并设置过期时间
     *
     * @param userInfo 载荷中的数据
     * @param privateKey 私钥
     * @param expiresTime 过期时间
     * @return token
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expiresTime) {
        return generateTokenExpireInMinutes(userInfo, privateKey, expiresTime, createJti());
    }

    /**
     * 使用私钥生成token,并设置过期时间和唯一id
     *
     * @param userInfo   载荷中的数据
     * @param privateKey 私钥
     * @param expiresTime 过期时间,单位分
     * @param id token唯一id
     * @return token
     */
    public static String generateTokenExpireInMinutes(Object userInfo, PrivateKey privateKey, int expiresTime, String id) {
        if (expiresTime <= 0) {
            expiresTime = EXPIRES_TOKEN;
        }
        //Header(头部)可以忽略不写
        return Jwts.builder()
                //设置id
                .setId(id)
                //Payload(载荷)
                .claim(JWT_KEY, JsonUtil.toString(userInfo))
                //签发时间
                .setIssuedAt(new Date())
                //过期时间
                .setExpiration(DateTime.now().plusMinutes(expiresTime).toDate())
                //签名方式(加密方式),null和SignatureAlgorithm.RS256,RS256是默认方式
                .signWith(privateKey, SignatureAlgorithm.RS256)
                .compact();
    }

    /**
     * 公钥解析token
     *
     * @param token     需要解析的token
     * @param publicKey 公钥
     */
    private static Claims parserToken(String token, PublicKey publicKey) throws ExpiredJwtException, UnsupportedJwtException, MalformedJwtException, IllegalArgumentException {
        return Jwts.parser().setSigningKey(publicKey).parseClaimsJws(token).getBody();
    }

    /**
     * 获取token中载荷信息
     *
     * @param token     需要解析的token
     * @param publicKey 公钥
     * @param <T>       用户信息对象的类型
     * @return 载荷信息
     */
    public static <T> Payload<T> getInfoFromToken(String token, PublicKey publicKey, Class<T> userType) {
        try {
            Claims body = parserToken(token, publicKey);
            Payload<T> claims = new Payload<>();
            claims.setId(body.getId());
            claims.setUserInfo(JsonUtil.toBean(body.get(JWT_KEY).toString(), userType));
            claims.setExpiration(body.getExpiration());
            return claims;
        }catch (Exception e) {
            return null;
        }
    }

    /**
     * 获取token中用户信息
     *
     * @param token     需要解析的token
     * @param publicKey 公钥
     * @param <T>       用户信息对象的类型
     * @return 用户信息
     */
    public static <T> T getUserInfo(String token, PublicKey publicKey, Class<T> tClass) {
        Payload<T> payload = getInfoFromToken(token, publicKey, tClass);
        return payload == null ? null : payload.getUserInfo();
    }
}
